<?php

namespace app\lib;

use think\Exception;
use think\facade\Config;

class MyLog
{
    // 配置参数
    private static $config = [
        'log_path' => '',
        'file_permission' => 0755,
        'date_format' => 'Ymd',
        'max_size' => 10485760, // 10MB
        'allowed_types' => ['error', 'info', 'debug', 'api']
    ];

    /**
     * 初始化配置
     */
    public static function init()
    {
        // 从配置文件加载日志配置
        $config = Config::get('log.custom_log', []);
        if (!empty($config)) {
            self::$config = array_merge(self::$config, $config);
        }

        // 设置默认日志路径
        if (empty(self::$config['log_path'])) {
            self::$config['log_path'] = public_path() . '/api_log/';
        }
    }

    /**
     * 错误日志记录
     * @param \Exception $e
     * @param string $title
     * @throws \Exception
     */
    public static function error($e, $title = '')
    {
        self::init();

        $data = [
            'title' => $title,
            'msg' => $e->getMessage(),
            'file' => $e->getFile(),
            'line' => $e->getLine(),
            'trace' => $e->getTraceAsString(),
            'params' => request()->param(),
            'header' => request()->header(),
        ];
        self::consoleLog($data, 'error/'.date('Ymd'));
    }

    /**
     * 通用日志记录
     * @param mixed $data 日志数据
     * @param string $type 日志类型
     * @param int $flags 文件写入标志
     * @throws \Exception
     */
    public static function consoleLog($data, $type = '', $flags = FILE_APPEND)
    {
        self::init();

        // 安全过滤日志类型
        $type = self::filterLogType($type);

        // 获取日志目录
        $dir = rtrim(self::$config['log_path'], '/') . '/' . $type . '/';

        // 创建目录
        self::createDirectory($dir);

        // 格式化数据
        $data = self::formatData($data);

        // 获取日志文件名
        $filename = $dir . date(self::$config['date_format']) . '.html';

        // 检查文件大小，超过限制则分卷
        $filename = self::checkFileSize($filename);

        // 生成HTML内容
        $html = self::generateHtml($data, $filename);

        // 写入文件
        self::writeToFile($filename, $html, $flags);
    }

    /**
     * 过滤日志类型，防止路径遍历
     * @param string $type
     * @return string
     */
    private static function filterLogType($type)
    {
        if (empty($type)) {
            return 'default';
        }

        // 替换危险字符
        $type = str_replace(['/', '\\', '..', ':', '*', '?', '"', '<', '>', '|'], '', $type);

        // // 检查是否在允许的类型列表中
        // $typeParts = explode('/', $type);
        // foreach ($typeParts as $part) {
        //     if (!empty(self::$config['allowed_types']) && !in_array($part, self::$config['allowed_types'])) {
        //         return 'default';
        //     }
        // }

        return $type;
    }

    /**
     * 创建目录
     * @param string $dir
     * @throws \Exception
     */
    private static function createDirectory($dir)
    {
        if (!is_dir($dir)) {
            if (!mkdir($dir, self::$config['file_permission'], true) && !is_dir($dir)) {
                throw new Exception('无法创建日志目录: ' . $dir);
            }
        }
    }

    /**
     * 格式化日志数据
     * @param mixed $data
     * @return string
     */
    private static function formatData($data)
    {
        if (is_array($data) || is_object($data)) {
            return json_encode($data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
        }
        return (string)$data;
    }

    /**
     * 检查文件大小，超过限制则分卷
     * @param string $filename
     * @return string
     */
    private static function checkFileSize($filename)
    {
        if (self::$config['max_size'] > 0 && file_exists($filename) && filesize($filename) > self::$config['max_size']) {
            $info = pathinfo($filename);
            $counter = 1;
            $newFilename = $info['dirname'] . '/' . $info['filename'] . '_' . $counter . '.' . $info['extension'];

            while (file_exists($newFilename) && filesize($newFilename) > self::$config['max_size']) {
                $counter++;
                $newFilename = $info['dirname'] . '/' . $info['filename'] . '_' . $counter . '.' . $info['extension'];
            }

            return $newFilename;
        }
        return $filename;
    }

    /**
     * 生成HTML内容
     * @param string $data
     * @param string $filename
     * @return string
     */
    private static function generateHtml($data, $filename)
    {
        $html = '';
        $isNewFile = !file_exists($filename);

        if ($isNewFile) {
            $html .= '<!DOCTYPE html><html><head>';
            $html .= '<meta http-equiv="Content-Type" content="text/html; charset=utf-8">';
            $html .= '<style>body{font-family:Arial,sans-serif;} .log-entry{margin:15px 0;padding:10px;border:1px solid #eee;border-radius:4px;} .log-header{color:#333;border-bottom:1px solid #eee;padding-bottom:5px;margin-bottom:5px;} .log-content{font-size:14px;}</style>';
            $html .= '</head><body>';
        }

        $html .= '<div class="log-entry">';
        $html .= '<div class="log-header">';
        $html .= '<h5 style="margin: 5px 0">============================================================================</h5>';
        $html .= '<h5 style="margin: 5px 0">执行日期：' . date('Y-m-d H:i:s', time()) . '</h5>';
        $html .= '<h5 style="margin: 5px 0">请求地址：<textarea style="width:100%;border:none;font-size:14px;font-family:monospace;">' . request()->domain() . request()->url() . '</textarea></h5>';
        $html .= '</div>';
        $html .= '<div class="log-content">';
        $html .= '<h5 style="margin: 5px 0">数据信息：<textarea style="width:100%;border:none;font-size:14px;font-family:monospace;height:150px;">' . htmlspecialchars($data) . '</textarea></h5>';
        $html .= '</div>';
        $html .= '</div>';

        if ($isNewFile) {
            $html .= '</body></html>';
        }

        return $html;
    }

    /**
     * 写入文件
     * @param string $filename
     * @param string $content
     * @param int $flags
     * @throws \Exception
     */
    private static function writeToFile($filename, $content, $flags)
    {
        $result = file_put_contents($filename, $content, $flags);
        // if ($result === false) {
        //     throw new Exception('无法写入日志文件: ' . $filename);
        // }
    }
}